Skip to content

Support requesting a specific IP address in cloudstack_ipaddress - #311

Merged
sureshanaparti merged 2 commits into
mainfrom
specifyIpAddress
Aug 17, 2026
Merged

Support requesting a specific IP address in cloudstack_ipaddress#311
sureshanaparti merged 2 commits into
mainfrom
specifyIpAddress

Conversation

@sudo87

@sudo87 sudo87 commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

cloudstack_ipaddress currently always lets CloudStack auto-select the next
free IP — there's no way to request a specific address. This blocks a common
pattern: an operator dedicates an intranet VLAN/IP range to an account, and
the tenant needs to acquire one particular address from it (e.g. because
firewall rules, DNS, or routing on the corporate side are already provisioned
for that exact IP).

The CloudStack API (associateIpAddress) already accepts an optional
ipaddress parameter; the provider never called it.

Fixes #291

Changes

  • ip_address changes from Computed-only to Optional + Computed + ForceNew.
    There's no CloudStack API to change an IP association in place, so a change
    to ip_address correctly triggers destroy+recreate.
  • resourceCloudStackIPAddressCreate passes the value through to
    AssociateIpAddressParams.SetIpaddress when set.
  • No changes to Read/Delete — the allocated address was already read back
    from the API response.
  • New acceptance test, TestAccCloudStackIPAddress_specificIP, requesting a
    specific address from a dedicated cloudstack_vlan_ip_range so the
    assertion is deterministic (the shared default pool can't guarantee a given
    address is free).
  • Documented the new argument in website/docs/r/ipaddress.html.markdown.

Backward compatibility

Since ip_address was Computed-only before this change, no existing
configuration could have set it — Terraform rejects a config value for a
Computed-only attribute at validate time. The schema change is purely
additive; existing state with ip_address populated shows no diff.

Testing

Verified against a simulator

  • All pre-existing cloudstack_ipaddress acceptance tests pass unchanged
    (_basic, _vpc, _vpcid_with_network_id).
  • New _specificIP test passes.
  • Downstream consumers unaffected: data_source_cloudstack_ipaddress,
    cloudstack_loadbalancer_rule (all 5 variants), cloudstack_static_nat.
  • Idempotent: terraform plan/apply with ip_address set and unchanged
    produces no diff and performs no actions; same when ip_address is
    omitted from config against existing state.
  • Requesting an address already in use, or one outside any configured range,
    both fail clearly with CloudStack API error 533: Insufficient address capacity — no CloudStack-side ambiguity to handle.
  • Changing ip_address correctly triggers ForceNew, scoped to only the
    cloudstack_ipaddress resource (the associated network is untouched).
  • Manually verified (not yet covered by the automated test):
    vpc_id + ip_address, and is_portable = true + ip_address — both work
    correctly and are idempotent.

@sudo87
sudo87 requested review from vishesh92 and a lite review from Copilot and removed request for Copilot August 12, 2026 06:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for requesting a specific public IP address in the cloudstack_ipaddress Terraform resource by exposing ip_address as a configurable argument and passing it through to CloudStack’s associateIpAddress API.

Changes:

  • Updated the cloudstack_ipaddress schema to make ip_address Optional + Computed + ForceNew.
  • Updated resource create logic to call AssociateIpAddressParams.SetIpaddress(...) when ip_address is provided.
  • Added documentation and a new acceptance test covering the new argument.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
cloudstack/resource_cloudstack_ipaddress.go Exposes ip_address as an optional ForceNew argument and forwards it to the CloudStack API on create.
cloudstack/resource_cloudstack_ipaddress_test.go Adds an acceptance test intended to verify requesting a specific IP address.
website/docs/r/ipaddress.html.markdown Documents the new ip_address argument and its ForceNew behavior.
Suppressed comments (2)

cloudstack/resource_cloudstack_ipaddress_test.go:211

  • To make TestAccCloudStackIPAddress_specificIP validate the new ip_address behavior, the dedicated VLAN range should include at least two addresses so CloudStack's default auto-allocation would likely choose a different IP than the one requested.
  end_ip               = "10.2.2.10"

cloudstack/resource_cloudstack_ipaddress_test.go:225

  • This should request the non-default address from the dedicated VLAN range (e.g. the range's end_ip) so the test exercises the ip_address request path rather than succeeding due to auto-selection.
  ip_address = cloudstack_vlan_ip_range.foo.start_ip

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread cloudstack/resource_cloudstack_ipaddress_test.go Outdated
Copilot AI review requested due to automatic review settings August 12, 2026 10:06

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (1)

cloudstack/resource_cloudstack_ipaddress_test.go:221

  • In the new acceptance test, cloudstack_network.foo sets source_nat_ip = true. That triggers an extra associateIpAddress during network creation, which can consume one of the IPs from the dedicated cloudstack_vlan_ip_range (and it does not depend on that range), making the test potentially flaky/non-deterministic. For this test, omit source_nat_ip so the only allocation is the cloudstack_ipaddress requesting the specific address.
resource "cloudstack_network" "foo" {
  name = "terraform-network"
  display_text = "terraform-network"
  cidr = "10.1.1.0/24"
  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
  source_nat_ip = true
  zone = "Sandbox-simulator"

@sureshanaparti sureshanaparti left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

clgtm

@kiranchavala kiranchavala left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM tested manually

Image
resource "cloudstack_network" "foo" {
  name             = "terraform-network"
  display_text     = "terraform-network"
  cidr             = "10.1.1.0/24"
  network_offering = "DefaultIsolatedNetworkOfferingWithSourceNatService"
  source_nat_ip    = true
  zone             = "4b6856cb-a3c3-4e9b-b0c4-f2c5bfa54667"
}

resource "cloudstack_ipaddress" "foo" {
  network_id = cloudstack_network.foo.id
  ip_address = "192.168.55.55"
}

terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  + create

Terraform will perform the following actions:

  # cloudstack_ipaddress.foo will be created
  + resource "cloudstack_ipaddress" "foo" {
      + id            = (known after apply)
      + ip_address    = "192.168.55.55"
      + is_portable   = false
      + is_source_nat = (known after apply)
      + network_id    = (known after apply)
      + project       = (known after apply)
      + tags          = (known after apply)
    }

  # cloudstack_network.foo will be created
  + resource "cloudstack_network" "foo" {
      + acl_id                = "none"
      + cidr                  = "10.1.1.0/24"
      + display_text          = "terraform-network"
      + endip                 = (known after apply)
      + gateway               = (known after apply)
      + id                    = (known after apply)
      + name                  = "terraform-network"
      + network_domain        = (known after apply)
      + network_offering      = "DefaultIsolatedNetworkOfferingWithSourceNatService"
      + project               = (known after apply)
      + source_nat_ip         = true
      + source_nat_ip_address = (known after apply)
      + source_nat_ip_id      = (known after apply)
      + startip               = (known after apply)
      + tags                  = (known after apply)
      + zone                  = "4b6856cb-a3c3-4e9b-b0c4-f2c5bfa54667"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

cloudstack_network.foo: Creating...
cloudstack_network.foo: Creation complete after 1s [id=d203173c-8ee2-4b43-94f1-509152e56437]
cloudstack_ipaddress.foo: Creating...
cloudstack_ipaddress.foo: Creation complete after 2s [id=033d3365-3b61-4de4-8ead-37116365bbc8]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

terraform destroy 
cloudstack_network.foo: Refreshing state... [id=d203173c-8ee2-4b43-94f1-509152e56437]
cloudstack_ipaddress.foo: Refreshing state... [id=033d3365-3b61-4de4-8ead-37116365bbc8]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated
with the following symbols:
  - destroy

Terraform will perform the following actions:

  # cloudstack_ipaddress.foo will be destroyed
  - resource "cloudstack_ipaddress" "foo" {
      - id            = "033d3365-3b61-4de4-8ead-37116365bbc8" -> null
      - ip_address    = "192.168.55.55" -> null
      - is_portable   = false -> null
      - is_source_nat = false -> null
      - network_id    = "d203173c-8ee2-4b43-94f1-509152e56437" -> null
      - tags          = {} -> null
        # (1 unchanged attribute hidden)
    }

  # cloudstack_network.foo will be destroyed
  - resource "cloudstack_network" "foo" {
      - acl_id                = "none" -> null
      - cidr                  = "10.1.1.0/24" -> null
      - display_text          = "terraform-network" -> null
      - gateway               = "10.1.1.1" -> null
      - id                    = "d203173c-8ee2-4b43-94f1-509152e56437" -> null
      - name                  = "terraform-network" -> null
      - network_domain        = "cs2cloud.internal" -> null
      - network_offering      = "DefaultIsolatedNetworkOfferingWithSourceNatService" -> null
      - source_nat_ip         = true -> null
      - source_nat_ip_address = "192.168.55.54" -> null
      - source_nat_ip_id      = "72475eaa-e023-450f-b0ac-b3f3d812f6cc" -> null
      - tags                  = {} -> null
      - zone                  = "4b6856cb-a3c3-4e9b-b0c4-f2c5bfa54667" -> null
        # (2 unchanged attributes hidden)
    }

Plan: 0 to add, 0 to change, 2 to destroy.

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

cloudstack_ipaddress.foo: Destroying... [id=033d3365-3b61-4de4-8ead-37116365bbc8]
cloudstack_ipaddress.foo: Destruction complete after 1s
cloudstack_network.foo: Destroying... [id=d203173c-8ee2-4b43-94f1-509152e56437]
cloudstack_network.foo: Destruction complete after 1s

Destroy complete! Resources: 2 destroyed.

@sureshanaparti
sureshanaparti merged commit 140aaf2 into main Aug 17, 2026
18 checks passed
@sureshanaparti
sureshanaparti deleted the specifyIpAddress branch August 17, 2026 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support requesting a specific IP address

4 participants